﻿using System.Collections.Generic;
using System.Windows;
using System;
using System.Linq;
using System.Windows.Media;
using System.Runtime.InteropServices;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
namespace ScatterView
{
    public class ScatterView : Panel
    {
        private Point _CursorPosition;
        private FrameworkElement _DraggingElement;
        private Dictionary<FrameworkElement, ElementStatus> _InternalChildren = new Dictionary<FrameworkElement, ElementStatus>();
        private DateTime _LastFrameTime;
        private int _NextHighestDepth;
        public static readonly DependencyProperty DragCalcTimeProperty = DependencyProperty.Register("DragCalcTime", typeof(TimeSpan), typeof(ScatterView), new PropertyMetadata(TimeSpan.FromMilliseconds(300.0)));
        public static readonly DependencyProperty DropDurationProperty = DependencyProperty.Register("DropDuration", typeof(TimeSpan), typeof(ScatterView), new PropertyMetadata(TimeSpan.FromMilliseconds(300.0)));
        public static readonly DependencyProperty FrictionProperty = DependencyProperty.Register("Friction", typeof(double), typeof(ScatterView), new PropertyMetadata(0.85));
        public static readonly DependencyProperty LiftDurationProperty = DependencyProperty.Register("LiftDuration", typeof(TimeSpan), typeof(ScatterView), new PropertyMetadata(TimeSpan.FromMilliseconds(500.0)));
        public static readonly DependencyProperty MinRotationDistanceProperty = DependencyProperty.Register("MinRotationDistance", typeof(double), typeof(ScatterView), new PropertyMetadata(30.0));
        public static readonly DependencyProperty RestitutionProperty = DependencyProperty.Register("Restitution", typeof(double), typeof(ScatterView), new PropertyMetadata(0.5));
        private MersenneTwister rnd = new MersenneTwister();
        public static readonly DependencyProperty SelectedChildrenProperty = DependencyProperty.Register("SelectedChildren", typeof(List<FrameworkElement>), typeof(ScatterView), new PropertyMetadata(new List<FrameworkElement>(), new PropertyChangedCallback(ScatterView.SelectedChildrenPropertyChanged)));
        public static readonly DependencyProperty SelectionTimeProperty = DependencyProperty.Register("SelectionTime", typeof(TimeSpan), typeof(ScatterView), new PropertyMetadata(TimeSpan.FromMilliseconds(500.0)));
        public static readonly DependencyProperty ShadowDistanceProperty = DependencyProperty.Register("ShadowDistance", typeof(double), typeof(ScatterView), new PropertyMetadata(25.0));

        // Events
        public event ScatterViewItemClickedHandler ScatterViewItemClicked;




        public ScatterView()
            : base()
        {
            this.rnd = new MersenneTwister();
            this._InternalChildren = new Dictionary<FrameworkElement, ElementStatus>();
            base.MouseMove += new MouseEventHandler(this.ScatterView_MouseMove);
            base.MouseLeftButtonUp += new MouseButtonEventHandler(this.ScatterView_MouseLeftButtonUp);
            base.LostMouseCapture += new MouseEventHandler(this.ScatterView_LostMouseCapture);
            this._LastFrameTime = DateTime.Now;
            CompositionTarget.Rendering += new EventHandler(this.CompositionTarget_Rendering);

        }
        public void AddChild(FrameworkElement child, FrameworkElement shadow)
        {
            child.MouseLeftButtonDown += new MouseButtonEventHandler(this.Child_MouseLeftButtonDown);
            TranslateTransform transform = new TranslateTransform();
            TranslateTransform transform2 = new TranslateTransform();
            RotateTransform transform3 = new RotateTransform();
            TranslateTransform transform4 = new TranslateTransform();
            child.RenderTransform = new TransformGroup { Children = new TransformCollection { transform, transform3, transform2, transform4 } };
            ElementStatus status = new ElementStatus
            {
                RegisterTranslate = transform,
                SpinRotate = transform3,
                SpinTranslate = transform2,
                DragTranslate = transform4
            };
            int num = this.rnd.Next(4);
            int maxValue = 40;
            int num3 = 40;
            double num4 = Math.Max(child.ActualWidth, child.ActualHeight);
            status.Introducing = true;
            status.SpinRotate.Angle = this.rnd.Next(360);
            switch (num)
            {
                case 0:
                    status.SpinTranslate.X = -num4;
                    status.SpinTranslate.Y = this.rnd.Next((int)base.ActualHeight);
                    status.Velocity = new Point((double)this.rnd.Next(maxValue), (double)this.rnd.Next(num3));
                    break;

                case 1:
                    status.SpinTranslate.X = this.rnd.Next((int)base.ActualWidth);
                    status.SpinTranslate.Y = -num4;
                    status.Velocity = new Point((double)this.rnd.Next(maxValue), (double)this.rnd.Next(num3));
                    break;

                case 2:
                    status.SpinTranslate.X = base.ActualWidth + num4;
                    status.SpinTranslate.Y = this.rnd.Next((int)base.ActualHeight);
                    status.Velocity = new Point((double)-this.rnd.Next(maxValue), (double)this.rnd.Next(num3));
                    break;

                case 3:
                    status.SpinTranslate.X = this.rnd.Next((int)base.ActualWidth);
                    status.SpinTranslate.Y = base.ActualHeight + num4;
                    status.Velocity = new Point((double)this.rnd.Next(maxValue), (double)-this.rnd.Next(num3));
                    break;
            }
            this._InternalChildren.Add(child, status);
            if (shadow != null)
            {
                status.Shadow = shadow;
                status.ShadowTranslate = new TranslateTransform();
                shadow.RenderTransform = new TransformGroup { Children = new TransformCollection { child.RenderTransform, status.ShadowTranslate } };
                this._NextHighestDepth++;
                base.Children.Add(shadow);
            }
            this._NextHighestDepth++;
            base.Children.Add(child);
            this.Drop(child);
            if (child is IScatterViewContent)
            {
                ((IScatterViewContent)child).Clicked += new RoutedEventHandler(this.ScatterView_Clicked);
            }
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement element in base.Children)
            {
                Point location = new Point();
                element.Arrange(new Rect(location, element.DesiredSize));
            }
            return finalSize;
        }

        private void Child_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            ElementStatus status = this._InternalChildren[element];
            Point point = e.GetPosition(this);
            status.DragOffset = point;
            status.SpinTranslate.X += status.DragTranslate.X;
            status.SpinTranslate.Y += status.DragTranslate.Y;
            status.DragTranslate.X = 0.0;
            status.DragTranslate.Y = 0.0;
            status.Velocity = new Point();
            status.DragPositions = new List<DragPosition>();
            DragPosition item = new DragPosition
            {
                Position = this._CursorPosition,
                Time = DateTime.Now
            };
            status.DragPositions.Add(item);
            Canvas.SetZIndex(element, ++this._NextHighestDepth);
            if (status.Shadow != null)
            {
                Canvas.SetZIndex(element, ++this._NextHighestDepth);
                Canvas.SetZIndex(status.Shadow, this._NextHighestDepth - 1);
            }
            Point worldCenter = this.GetWorldCenter(element);
            double x = point.X - worldCenter.X;
            double y = point.Y - worldCenter.Y;
            status.OriginalDistanceToContact = Math.Sqrt((x * x) + (y * y));
            status.LastRadiansToContact = Math.Atan2(y, x);
            status.ToggleTime = DateTime.Now;
            status.ToggleShadowProgress = status.ShadowProgress;
            base.CaptureMouse();
            this._DraggingElement = element;
            this.Lift(this._DraggingElement);
        }

        private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (this._DraggingElement != null)
            {
                ElementStatus status = this._InternalChildren[this._DraggingElement];
                DragPosition item = new DragPosition
                {
                    Position = this._CursorPosition,
                    Time = DateTime.Now
                };
                status.DragPositions.Add(item);
            }
            this._LastFrameTime = DateTime.Now;
            double friction = this.Friction;
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status2 = this._InternalChildren[element];
                status2.RegisterTranslate.X = -element.ActualWidth / 2.0;
                status2.RegisterTranslate.Y = -element.ActualHeight / 2.0;
                if (status2.Shadow != null)
                {
                    FrameworkElement element2 = element;
                    double x = (status2.SpinTranslate.X + status2.DragTranslate.X) - (base.ActualWidth / 2.0);
                    double y = (status2.SpinTranslate.Y + status2.DragTranslate.Y) - (base.ActualHeight / 2.0);
                    double d = Math.Atan2(y, x);
                    double num5 = Math.Sqrt((x * x) + (y * y));
                    status2.Shadow.Width = element2.ActualWidth;
                    status2.Shadow.Height = element2.ActualHeight;
                    double num6 = (element2 == this._DraggingElement) ? (1.0 - status2.ToggleShadowProgress) : -status2.ToggleShadowProgress;
                    TimeSpan span = (element2 == this._DraggingElement) ? this.LiftDuration : this.DropDuration;
                    double num7 = span.TotalMilliseconds * Math.Abs(num6);
                    TimeSpan span2 = (TimeSpan)(DateTime.Now - status2.ToggleTime);
                    double totalMilliseconds = span2.TotalMilliseconds;
                    if (totalMilliseconds >= num7)
                    {
                        status2.ShadowProgress = (element2 == this._DraggingElement) ? ((double)1) : ((double)0);
                    }
                    else
                    {
                        status2.ShadowProgress = QuadEaseOut(totalMilliseconds, status2.ToggleShadowProgress, num6, num7);
                    }
                    status2.ShadowTranslate.X = Math.Cos(d) * -Math.Min(num5, status2.ShadowProgress * this.ShadowDistance);
                    status2.ShadowTranslate.Y = Math.Sin(d) * -Math.Min(num5, status2.ShadowProgress * this.ShadowDistance);
                }
                if (element != this._DraggingElement)
                {
                    Point worldCenter = this.GetWorldCenter(element);
                    if (Math.Abs(status2.Velocity.Y) > 0.0)
                    {
                        if (!status2.Introducing)
                        {
                            if (worldCenter.Y <= 0.0)
                            {
                                status2.DragTranslate.Y += -worldCenter.Y;
                                status2.Velocity.Y *= -this.Restitution;
                            }
                            else if (worldCenter.Y > base.ActualHeight)
                            {
                                status2.DragTranslate.Y -= worldCenter.Y - base.ActualHeight;
                                status2.Velocity.Y *= -this.Restitution;
                            }
                        }
                        status2.DragTranslate.Y += status2.Velocity.Y;
                        if (Math.Abs(status2.Velocity.Y) < 0.1)
                        {
                            status2.Velocity.Y = 0.0;
                        }
                        status2.Velocity.Y *= friction;
                    }
                    if (Math.Abs(status2.Velocity.X) > 0.0)
                    {
                        if (!status2.Introducing)
                        {
                            if (worldCenter.X <= 0.0)
                            {
                                status2.DragTranslate.X += -worldCenter.X;
                                status2.Velocity.X *= -this.Restitution;
                            }
                            else if (worldCenter.X > base.ActualWidth)
                            {
                                status2.DragTranslate.X -= worldCenter.X - base.ActualWidth;
                                status2.Velocity.X *= -this.Restitution;
                            }
                        }
                        status2.DragTranslate.X += status2.Velocity.X;
                        if (Math.Abs(status2.Velocity.X) < 0.1)
                        {
                            status2.Velocity.X = 0.0;
                        }
                        status2.Velocity.X *= friction;
                    }
                    Point point2 = new Point();
                    if (status2.TranslateBy != point2)
                    {
                        TimeSpan span3 = (TimeSpan)(DateTime.Now - status2.MoveBegin);
                        double t = span3.TotalMilliseconds;
                        status2.DragTranslate.X = QuadEaseOut(t, 0.0, status2.TranslateBy.X, this.SelectionTime.TotalMilliseconds);
                        status2.DragTranslate.Y = QuadEaseOut(t, 0.0, status2.TranslateBy.Y, this.SelectionTime.TotalMilliseconds);
                        status2.SpinRotate.Angle = QuadEaseOut(t, status2.RotateFrom, status2.RotateTo - status2.RotateFrom, this.SelectionTime.TotalMilliseconds);
                        if (t > this.SelectionTime.TotalMilliseconds)
                        {
                            status2.DragTranslate.X = status2.TranslateBy.X;
                            status2.DragTranslate.Y = status2.TranslateBy.Y;
                            status2.SpinRotate.Angle = status2.RotateFrom + (status2.RotateTo - status2.RotateFrom);
                            status2.TranslateBy = new Point();
                        }
                    }
                    Rect rect = new Rect(new Point(0.0, 0.0), new Size(base.ActualWidth, base.ActualHeight));
                    if (rect.Contains(worldCenter))
                    {
                        status2.Introducing = false;
                    }
                }
            }
        }

        private Point ComputeVelocity(List<DragPosition> positions, DateTime endTime)
        {
            Point point = new Point();
            int num = 0;
            int num2 = 0;
            for (int i = 1; i < positions.Count; i++)
            {
                Point position = positions[i - 1].Position;
                Point point3 = positions[i].Position;
                DateTime time = positions[i].Time;
                if ((endTime - time) <= this.DragCalcTime)
                {
                    double num4 = point3.X - position.X;
                    if (num4 == 0.0)
                    {
                        point.X = 0.0;
                        num = 0;
                    }
                    else if ((i >= 2) && (Math.Sign(num4) != Math.Sign((double)(position.X - positions[i - 2].Position.X))))
                    {
                        point.X = point3.X - position.X;
                        num = 1;
                    }
                    else
                    {
                        point.X += point3.X - position.X;
                        num++;
                    }
                    double num5 = point3.Y - position.Y;
                    if (num5 == 0.0)
                    {
                        point.Y = 0.0;
                        num2 = 0;
                    }
                    else if (((i >= 2) && (Math.Sign(num5) != 0)) && (Math.Sign(num5) != Math.Sign((double)(position.Y - positions[i - 2].Position.Y))))
                    {
                        point.Y = point3.Y - position.Y;
                        num2 = 1;
                    }
                    else
                    {
                        point.Y += point3.Y - position.Y;
                        num2++;
                    }
                }
            }
            point.X = (num == 0) ? 0.0 : (point.X / ((double)num));
            point.Y = (num2 == 0) ? 0.0 : (point.Y / ((double)num2));
            return point;
        }

        private void Drop(FrameworkElement child)
        {
            if (child is IScatterViewShadow)
            {
                ((IScatterViewShadow)child).Drop();
            }
            else
            {
                Storyboard storyboard = child.Resources["Drop"] as Storyboard;
                if (storyboard != null)
                {
                    storyboard.Begin();
                }
            }
            ElementStatus status = this._InternalChildren[child];
            if (status.Shadow != null)
            {
                if (status.Shadow is IScatterViewShadow)
                {
                    ((IScatterViewShadow)status.Shadow).Drop();
                }
                else
                {
                    Storyboard storyboard2 = status.Shadow.Resources["Drop"] as Storyboard;
                    if (storyboard2 != null)
                    {
                        storyboard2.Begin();
                    }
                }
            }
        }

        private void EndDrag()
        {
            if (this._DraggingElement != null)
            {
                ElementStatus status = this._InternalChildren[this._DraggingElement];
                status.Velocity = this.ComputeVelocity(status.DragPositions, DateTime.Now);
                status.DragPositions = null;
                status.ToggleTime = DateTime.Now;
                status.ToggleShadowProgress = status.ShadowProgress;
                this.Drop(this._DraggingElement);
                this._DraggingElement = null;
                base.ReleaseMouseCapture();
            }
        }

        public List<FrameworkElement> GetAllChildren()
        {
            return new List<FrameworkElement>(this._InternalChildren.Keys);
        }

        private Point GetWorldCenter(UIElement element)
        {
            return element.TransformToVisual(this).Transform(new Point(((FrameworkElement)element).ActualWidth / 2.0, ((FrameworkElement)element).ActualHeight / 2.0));
        }

        private void Lift(FrameworkElement child)
        {
            if (child is IScatterViewShadow)
            {
                ((IScatterViewShadow)child).Lift();
            }
            else
            {
                Storyboard storyboard = child.Resources["Lift"] as Storyboard;
                if (storyboard != null)
                {
                    storyboard.Begin();
                }
            }
            ElementStatus status = this._InternalChildren[child];
            if (status.Shadow != null)
            {
                if (status.Shadow is IScatterViewShadow)
                {
                    ((IScatterViewShadow)status.Shadow).Lift();
                }
                else
                {
                    Storyboard storyboard2 = status.Shadow.Resources["Lift"] as Storyboard;
                    if (storyboard2 != null)
                    {
                        storyboard2.Begin();
                    }
                }
            }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            foreach (UIElement element in base.Children)
            {
                element.Measure(availableSize);
            }
            base.Clip = new RectangleGeometry { Rect = new Rect(new Point(), availableSize) };
            return availableSize;
        }

        public static double QuadEaseOut(double t, double b, double c, double d)
        {
            return (((-c * (t /= d)) * (t - 2.0)) + b);
        }

        public void RemoveAllChildren()
        {
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                element.MouseLeftButtonDown -= new MouseButtonEventHandler(this.Child_MouseLeftButtonDown);
                base.Children.Remove(element);
                base.Children.Remove(this._InternalChildren[element].Shadow);
            }
            _InternalChildren = new Dictionary<FrameworkElement, ElementStatus>();
        }

        public void RemoveChild(FrameworkElement child)
        {
            child.MouseLeftButtonDown -= new MouseButtonEventHandler(this.Child_MouseLeftButtonDown);
            base.Children.Remove(child);
            base.Children.Remove(this._InternalChildren[child].Shadow);
            this._InternalChildren.Remove(child);
        }

        private void ScatterView_Clicked(object sender, RoutedEventArgs e)
        {
            if (this.ScatterViewItemClicked != null)
            {
                this.ScatterViewItemClicked(this, new ScatterViewItemClickedEventArgs((FrameworkElement)sender));
            }
        }

        private void ScatterView_LostMouseCapture(object sender, MouseEventArgs e)
        {
            this.EndDrag();
        }

        private void ScatterView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.EndDrag();
        }

        private void ScatterView_MouseMove(object sender, MouseEventArgs e)
        {
            this._CursorPosition = e.GetPosition(this);
            if (this._DraggingElement != null)
            {
                ElementStatus status = this._InternalChildren[this._DraggingElement];
                Point position = e.GetPosition(this);
                if (status.OriginalDistanceToContact <= this.MinRotationDistance)
                {
                    Point point2 = new Point(position.X - status.DragOffset.X, position.Y - status.DragOffset.Y);
                    if (((status.DragOffset.X + point2.X) > 0.0) && ((status.DragOffset.X + point2.X) < base.ActualWidth))
                    {
                        status.DragTranslate.X = point2.X;
                    }
                    if (((status.DragOffset.Y + point2.Y) > 0.0) && ((status.DragOffset.Y + point2.Y) < base.ActualHeight))
                    {
                        status.DragTranslate.Y = point2.Y;
                    }
                }
                else
                {
                    double x = position.X - status.SpinTranslate.X;
                    double y = position.Y - status.SpinTranslate.Y;
                    double d = Math.Atan2(y, x);
                    double num4 = d - status.LastRadiansToContact;
                    double num5 = num4 * 57.295779513082323;
                    double num6 = Math.Cos(d) * status.OriginalDistanceToContact;
                    double num7 = Math.Sin(d) * status.OriginalDistanceToContact;
                    Point point3 = new Point(status.SpinTranslate.X, status.SpinTranslate.Y);
                    status.SpinRotate.Angle += num5;
                    status.SpinTranslate.X = position.X - num6;
                    status.SpinTranslate.Y = position.Y - num7;
                    if (((status.SpinTranslate.X < 0.0) || (status.SpinTranslate.X > base.ActualWidth)) || ((status.SpinTranslate.Y < 0.0) || (status.SpinTranslate.Y > base.ActualHeight)))
                    {
                        status.SpinTranslate.X = point3.X;
                        status.SpinTranslate.Y = point3.Y;
                        status.SpinRotate.Angle -= num5;
                    }
                    else
                    {
                        status.LastRadiansToContact = d;
                    }
                }
            }
        }

        private static void SelectedChildrenPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((ScatterView)sender).UpdateSelectedChildren(e.OldValue as List<FrameworkElement>);
        }

        private void ShuffleChildren()
        {
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status = this._InternalChildren[element];
                status.TranslateBy = new Point(this.rnd.Next((int)base.ActualWidth) - status.SpinTranslate.X, this.rnd.Next((int)base.ActualHeight) - status.SpinTranslate.Y);
                status.RotateFrom = status.SpinRotate.Angle;
                status.RotateTo = status.RotateFrom + (this.rnd.Next(0x2d) * (((base.Children.IndexOf(element) % 2) == 0) ? 1 : -1));
                status.Velocity = new Point();
                status.MoveBegin = DateTime.Now;
            }
        }

        private void UpdateSelectedChildren(List<FrameworkElement> lastSelectedChildren)
        {
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status = this._InternalChildren[element];
                status.SpinTranslate.X += status.DragTranslate.X;
                status.SpinTranslate.Y += status.DragTranslate.Y;
                status.DragTranslate.X = 0.0;
                status.DragTranslate.Y = 0.0;
            }
            if ((this.SelectedChildren.Count == 0) || (this.SelectedChildren.Count == this._InternalChildren.Count))
            {
                this.ShuffleChildren();
            }
            else
            {
                int num = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
                int num2 = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
                double num3 = 5.0;
                List<List<Rect>> list = new List<List<Rect>>();
                Rect rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
                list = new List<List<Rect>>();
                rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
                for (int i = 0; i < num2; i++)
                {
                    List<Rect> item = new List<Rect>();
                    double x = 0.0;
                    double num6 = 0.0;
                    FrameworkElement element2 = null;
                    for (int k = 0; k < num; k++)
                    {
                        try
                        {
                            element2 = this.SelectedChildren[(i * num) + k];
                        }
                        catch
                        {
                            break;
                        }
                        Rect rect2 = new Rect(new Point(x, rect.Height), new Size(element2.ActualWidth + num3, element2.ActualHeight + num3));
                        x += rect2.Width;
                        num6 = Math.Max(num6, rect2.Height);
                        item.Add(rect2);
                    }
                    if (element2 == null)
                    {
                        break;
                    }
                    list.Add(item);
                    rect.Height += num6;
                    rect.Width = Math.Max(rect.Width, x);
                }
                foreach (List<Rect> list3 in list)
                {
                    double num8 = 0.0;
                    double num9 = 0.0;
                    foreach (Rect rect3 in list3)
                    {
                        num8 += rect3.Width;
                        num9 = Math.Max(rect3.Height, num9);
                    }
                    for (int m = 0; m < list3.Count; m++)
                    {
                        Rect rect4 = list3[m];
                        rect4.X += (rect.Width - num8) / 2.0;
                        rect4.Y += (num9 - rect4.Height) / 2.0;
                        rect4.X += (base.ActualWidth - rect.Width) / 2.0;
                        rect4.Y += (base.ActualHeight - rect.Height) / 2.0;
                        rect.X = Math.Min(rect4.X, rect.X);
                        rect.Y = Math.Min(rect4.Y, rect.Y);
                        list3[m] = rect4;
                    }
                }
                int num11 = 0;
                for (int j = 0; j < num2; j++)
                {
                    FrameworkElement element3 = null;
                    for (int n = 0; n < num; n++)
                    {
                        try
                        {
                            element3 = this.SelectedChildren[(j * num) + n];
                        }
                        catch
                        {
                            break;
                        }
                        ElementStatus status2 = this._InternalChildren[element3];
                        Rect rect5 = list[j][n];
                        status2.TranslateBy = new Point((rect5.X + (rect5.Width / 2.0)) - status2.SpinTranslate.X, (rect5.Y + (rect5.Height / 2.0)) - status2.SpinTranslate.Y);
                        status2.RotateFrom = status2.SpinRotate.Angle;
                        status2.RotateTo = (status2.SpinRotate.Angle <= 180.0) ? ((double)0) : ((double)360);
                        status2.Velocity = new Point();
                        status2.MoveBegin = DateTime.Now;
                        num11++;
                        Canvas.SetZIndex(status2.Shadow, ++this._NextHighestDepth);
                        Canvas.SetZIndex(element3, ++this._NextHighestDepth);
                    }
                    if (element3 == null)
                    {
                        break;
                    }
                }
                List<FrameworkElement> list4 = (from child in this._InternalChildren.Keys
                                                where !this.SelectedChildren.Contains(child)
                                                select child).ToList<FrameworkElement>();
                double num14 = 100.0;
                double num15 = 10.0;
                Rect rect6 = new Rect(new Point(Math.Max(num15, rect.X - num14), Math.Max(num15, rect.Y - num14)), new Size(Math.Min((double)(base.ActualWidth - num15), (double)(rect.Width + (num14 * 2.0))), Math.Min((double)(base.ActualHeight - num15), (double)(rect.Height + (num14 * 2.0)))));
                foreach (FrameworkElement element4 in list4)
                {
                    ElementStatus status3 = this._InternalChildren[element4];
                    Point point = new Point(status3.SpinTranslate.X, status3.SpinTranslate.Y);
                    if (((rect6.Contains(point) && (point.X != rect6.Right)) && (((point.X != rect6.Left) && (point.Y != rect6.Top)) && (point.Y != rect6.Bottom))) || lastSelectedChildren.Contains(element4))
                    {
                        double num16 = Math.Abs((double)(point.X - rect.Left));
                        double num17 = Math.Abs((double)(point.Y - rect.Top));
                        double num18 = Math.Abs((double)(rect.Right - point.X));
                        double num19 = Math.Abs((double)(rect.Bottom - point.Y));
                        double num20 = Math.Min(Math.Min(num16, num18), Math.Min(num17, num19));
                        Point point2 = new Point();
                        if (num20 == num16)
                        {
                            point2.X = this.rnd.Next((int)rect6.Left);
                            point2.Y = this.rnd.Next((int)base.ActualHeight);
                        }
                        else if (num20 == num17)
                        {
                            point2.X = this.rnd.Next((int)base.ActualWidth);
                            point2.Y = this.rnd.Next((int)rect6.Top);
                        }
                        else if (num20 == num18)
                        {
                            point2.X = rect6.Right + this.rnd.Next((int)(base.ActualWidth - rect6.Right));
                            point2.Y = this.rnd.Next((int)base.ActualHeight);
                        }
                        else if (num20 == num19)
                        {
                            point2.X = this.rnd.Next((int)base.ActualWidth);
                            point2.Y = rect6.Bottom + this.rnd.Next((int)(base.ActualHeight - rect6.Bottom));
                        }
                        point2.X = Math.Min(Math.Max(0.0, point2.X), base.ActualWidth);
                        point2.Y = Math.Min(Math.Max(0.0, point2.Y), base.ActualHeight);
                        point2.X -= status3.SpinTranslate.X;
                        point2.Y -= status3.SpinTranslate.Y;
                        status3.TranslateBy = point2;
                        status3.RotateFrom = status3.SpinRotate.Angle;
                        status3.RotateTo = status3.RotateFrom + (this.rnd.Next(0x2d) * (((base.Children.IndexOf(element4) % 2) == 0) ? 1 : -1));
                        status3.Velocity = new Point();
                        status3.MoveBegin = DateTime.Now;
                    }
                }
            }
        }
        
        public void ShuffeView() {
            foreach (FrameworkElement element in this._InternalChildren.Keys)
            {
                ElementStatus status = this._InternalChildren[element];
                status.SpinTranslate.X += status.DragTranslate.X;
                status.SpinTranslate.Y += status.DragTranslate.Y;
                status.DragTranslate.X = 0.0;
                status.DragTranslate.Y = 0.0;
            }
            if ((this.SelectedChildren.Count == 0) || (this.SelectedChildren.Count == this._InternalChildren.Count))
            {
                this.ShuffleChildren();
            }
            else
            {
                int num = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
                int num2 = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
                double num3 = 5.0;
                List<List<Rect>> list = new List<List<Rect>>();
                Rect rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
                list = new List<List<Rect>>();
                rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
                for (int i = 0; i < num2; i++)
                {
                    List<Rect> item = new List<Rect>();
                    double x = 0.0;
                    double num6 = 0.0;
                    FrameworkElement element2 = null;
                    for (int k = 0; k < num; k++)
                    {
                        try
                        {
                            element2 = this.SelectedChildren[(i * num) + k];
                        }
                        catch
                        {
                            break;
                        }
                        Rect rect2 = new Rect(new Point(x, rect.Height), new Size(element2.ActualWidth + num3, element2.ActualHeight + num3));
                        x += rect2.Width;
                        num6 = Math.Max(num6, rect2.Height);
                        item.Add(rect2);
                    }
                    if (element2 == null)
                    {
                        break;
                    }
                    list.Add(item);
                    rect.Height += num6;
                    rect.Width = Math.Max(rect.Width, x);
                }
                foreach (List<Rect> list3 in list)
                {
                    double num8 = 0.0;
                    double num9 = 0.0;
                    foreach (Rect rect3 in list3)
                    {
                        num8 += rect3.Width;
                        num9 = Math.Max(rect3.Height, num9);
                    }
                    for (int m = 0; m < list3.Count; m++)
                    {
                        Rect rect4 = list3[m];
                        rect4.X += (rect.Width - num8) / 2.0;
                        rect4.Y += (num9 - rect4.Height) / 2.0;
                        rect4.X += (base.ActualWidth - rect.Width) / 2.0;
                        rect4.Y += (base.ActualHeight - rect.Height) / 2.0;
                        rect.X = Math.Min(rect4.X, rect.X);
                        rect.Y = Math.Min(rect4.Y, rect.Y);
                        list3[m] = rect4;
                    }
                }
                int num11 = 0;
                for (int j = 0; j < num2; j++)
                {
                    FrameworkElement element3 = null;
                    for (int n = 0; n < num; n++)
                    {
                        try
                        {
                            element3 = this.SelectedChildren[(j * num) + n];
                        }
                        catch
                        {
                            break;
                        }
                        ElementStatus status2 = this._InternalChildren[element3];
                        Rect rect5 = list[j][n];
                        status2.TranslateBy = new Point((rect5.X + (rect5.Width / 2.0)) - status2.SpinTranslate.X, (rect5.Y + (rect5.Height / 2.0)) - status2.SpinTranslate.Y);
                        status2.RotateFrom = status2.SpinRotate.Angle;
                        status2.RotateTo = (status2.SpinRotate.Angle <= 180.0) ? ((double)0) : ((double)360);
                        status2.Velocity = new Point();
                        status2.MoveBegin = DateTime.Now;
                        num11++;
                        Canvas.SetZIndex(status2.Shadow, ++this._NextHighestDepth);
                        Canvas.SetZIndex(element3, ++this._NextHighestDepth);
                    }
                    if (element3 == null)
                    {
                        break;
                    }
                }
            }
        }
        public void TileView()
        {
            SelectedChildren = GetAllChildren();
            List<FrameworkElement> lastSelectedChildren = null;
            int num = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
            int num2 = (int)Math.Ceiling(Math.Sqrt((double)this.SelectedChildren.Count));
            double num3 = 5.0;
            List<List<Rect>> list = new List<List<Rect>>();
            Rect rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
            list = new List<List<Rect>>();
            rect = new Rect(new Point(base.ActualWidth, base.ActualHeight), new Size());
            for (int i = 0; i < num2; i++)
            {
                List<Rect> item = new List<Rect>();
                double x = 0.0;
                double num6 = 0.0;
                FrameworkElement element2 = null;
                for (int k = 0; k < num; k++)
                {
                    try
                    {
                        element2 = this.SelectedChildren[(i * num) + k];
                    }
                    catch
                    {
                        break;
                    }
                    Rect rect2 = new Rect(new Point(x, rect.Height), new Size(element2.ActualWidth + num3, element2.ActualHeight + num3));
                    x += rect2.Width;
                    num6 = Math.Max(num6, rect2.Height);
                    item.Add(rect2);
                }
                if (element2 == null)
                {
                    break;
                }
                list.Add(item);
                rect.Height += num6;
                rect.Width = Math.Max(rect.Width, x);
            }
            foreach (List<Rect> list3 in list)
            {
                double num8 = 0.0;
                double num9 = 0.0;
                foreach (Rect rect3 in list3)
                {
                    num8 += rect3.Width;
                    num9 = Math.Max(rect3.Height, num9);
                }
                for (int m = 0; m < list3.Count; m++)
                {
                    Rect rect4 = list3[m];
                    rect4.X += (rect.Width - num8) / 2.0;
                    rect4.Y += (num9 - rect4.Height) / 2.0;
                    rect4.X += (base.ActualWidth - rect.Width) / 2.0;
                    rect4.Y += (base.ActualHeight - rect.Height) / 2.0;
                    rect.X = Math.Min(rect4.X, rect.X);
                    rect.Y = Math.Min(rect4.Y, rect.Y);
                    list3[m] = rect4;
                }
            }
            int num11 = 0;
            for (int j = 0; j < num2; j++)
            {
                FrameworkElement element3 = null;
                for (int n = 0; n < num; n++)
                {
                    try
                    {
                        element3 = this.SelectedChildren[(j * num) + n];
                    }
                    catch
                    {
                        break;
                    }
                    ElementStatus status2 = this._InternalChildren[element3];
                    Rect rect5 = list[j][n];
                    status2.TranslateBy = new Point((rect5.X + (rect5.Width / 2.0)) - status2.SpinTranslate.X, (rect5.Y + (rect5.Height / 2.0)) - status2.SpinTranslate.Y);
                    status2.RotateFrom = status2.SpinRotate.Angle;
                    status2.RotateTo = (status2.SpinRotate.Angle <= 180.0) ? ((double)0) : ((double)360);
                    status2.Velocity = new Point();
                    status2.MoveBegin = DateTime.Now;
                    num11++;
                    Canvas.SetZIndex(status2.Shadow, ++this._NextHighestDepth);
                    Canvas.SetZIndex(element3, ++this._NextHighestDepth);
                }
                if (element3 == null)
                {
                    break;
                }
            }
            List<FrameworkElement> list4 = (from child in this._InternalChildren.Keys
                                            where !this.SelectedChildren.Contains(child)
                                            select child).ToList<FrameworkElement>();
            double num14 = 100.0;
            double num15 = 10.0;
            Rect rect6 = new Rect(new Point(Math.Max(num15, rect.X - num14), Math.Max(num15, rect.Y - num14)), new Size(Math.Min((double)(base.ActualWidth - num15), (double)(rect.Width + (num14 * 2.0))), Math.Min((double)(base.ActualHeight - num15), (double)(rect.Height + (num14 * 2.0)))));
            foreach (FrameworkElement element4 in list4)
            {
                ElementStatus status3 = this._InternalChildren[element4];
                Point point = new Point(status3.SpinTranslate.X, status3.SpinTranslate.Y);
                if (((rect6.Contains(point) && (point.X != rect6.Right)) && (((point.X != rect6.Left) && (point.Y != rect6.Top)) && (point.Y != rect6.Bottom))) || lastSelectedChildren.Contains(element4))
                {
                    double num16 = Math.Abs((double)(point.X - rect.Left));
                    double num17 = Math.Abs((double)(point.Y - rect.Top));
                    double num18 = Math.Abs((double)(rect.Right - point.X));
                    double num19 = Math.Abs((double)(rect.Bottom - point.Y));
                    double num20 = Math.Min(Math.Min(num16, num18), Math.Min(num17, num19));
                    Point point2 = new Point();
                    if (num20 == num16)
                    {
                        point2.X = this.rnd.Next((int)rect6.Left);
                        point2.Y = this.rnd.Next((int)base.ActualHeight);
                    }
                    else if (num20 == num17)
                    {
                        point2.X = this.rnd.Next((int)base.ActualWidth);
                        point2.Y = this.rnd.Next((int)rect6.Top);
                    }
                    else if (num20 == num18)
                    {
                        point2.X = rect6.Right + this.rnd.Next((int)(base.ActualWidth - rect6.Right));
                        point2.Y = this.rnd.Next((int)base.ActualHeight);
                    }
                    else if (num20 == num19)
                    {
                        point2.X = this.rnd.Next((int)base.ActualWidth);
                        point2.Y = rect6.Bottom + this.rnd.Next((int)(base.ActualHeight - rect6.Bottom));
                    }
                    point2.X = Math.Min(Math.Max(0.0, point2.X), base.ActualWidth);
                    point2.Y = Math.Min(Math.Max(0.0, point2.Y), base.ActualHeight);
                    point2.X -= status3.SpinTranslate.X;
                    point2.Y -= status3.SpinTranslate.Y;
                    status3.TranslateBy = point2;
                    status3.RotateFrom = status3.SpinRotate.Angle;
                    status3.RotateTo = status3.RotateFrom + (this.rnd.Next(0x2d) * (((base.Children.IndexOf(element4) % 2) == 0) ? 1 : -1));
                    status3.Velocity = new Point();
                    status3.MoveBegin = DateTime.Now;
                }
            }
        }
        public TimeSpan DragCalcTime
        {
            get
            {
                return (TimeSpan)base.GetValue(DragCalcTimeProperty);
            }
            set
            {
                base.SetValue(DragCalcTimeProperty, value);
            }
        }

        public TimeSpan DropDuration
        {
            get
            {
                return (TimeSpan)base.GetValue(DropDurationProperty);
            }
            set
            {
                base.SetValue(DropDurationProperty, value);
            }
        }

        public double Friction
        {
            get
            {
                return (double)base.GetValue(FrictionProperty);
            }
            set
            {
                base.SetValue(FrictionProperty, value);
            }
        }

        public TimeSpan LiftDuration
        {
            get
            {
                return (TimeSpan)base.GetValue(LiftDurationProperty);
            }
            set
            {
                base.SetValue(LiftDurationProperty, value);
            }
        }

        public double MinRotationDistance
        {
            get
            {
                return (double)base.GetValue(MinRotationDistanceProperty);
            }
            set
            {
                base.SetValue(MinRotationDistanceProperty, value);
            }
        }

        public double Restitution
        {
            get
            {
                return (double)base.GetValue(RestitutionProperty);
            }
            set
            {
                base.SetValue(RestitutionProperty, value);
            }
        }

        public List<FrameworkElement> SelectedChildren
        {
            get
            {
                return (List<FrameworkElement>)base.GetValue(SelectedChildrenProperty);
            }
            set
            {
                base.SetValue(SelectedChildrenProperty, value);
            }
        }

        public TimeSpan SelectionTime
        {
            get
            {
                return (TimeSpan)base.GetValue(SelectionTimeProperty);
            }
            set
            {
                base.SetValue(SelectionTimeProperty, value);
            }
        }

        public double ShadowDistance
        {
            get
            {
                return (double)base.GetValue(ShadowDistanceProperty);
            }
            set
            {
                base.SetValue(ShadowDistanceProperty, value);
            }
        }



        [StructLayout(LayoutKind.Sequential)]
        private struct DragPosition
        {
            internal Point Position;
            internal DateTime Time;
        }

        private class ElementStatus
        {
            // Fields
            internal Point DragOffset;
            internal List<ScatterView.DragPosition> DragPositions;
            internal TranslateTransform DragTranslate;
            internal bool Introducing;
            internal double LastRadiansToContact;
            internal DateTime MoveBegin;
            internal double OriginalDistanceToContact;
            internal TranslateTransform RegisterTranslate;
            internal double RotateFrom;
            internal double RotateTo;
            internal FrameworkElement Shadow;
            internal double ShadowProgress = 1.0;
            internal TranslateTransform ShadowTranslate;
            internal RotateTransform SpinRotate;
            internal TranslateTransform SpinTranslate;
            internal double ToggleShadowProgress = 1.0;
            internal DateTime ToggleTime = DateTime.Now;
            internal Point TranslateBy;
            internal Point Velocity;
        }


        public class ScatterViewItemClickedEventArgs : RoutedEventArgs
        {
            // Fields
            public readonly FrameworkElement ClickedItem;

            // Methods
            public ScatterViewItemClickedEventArgs(FrameworkElement clickedItem)
            {
                this.ClickedItem = clickedItem;
            }
        }

        public delegate void ScatterViewItemClickedHandler(ScatterView sender, ScatterView.ScatterViewItemClickedEventArgs e);

    }
}